xlv.obj.ulli={
	extend:function(){
	},
	implement:function(){
		this.implement('xlv.imp.mvc');
		this.implement('xlv.imp.select');
		this.implement('xlv.imp.edc');
	},
	create:function(tag){
		var div=document.createElement((tag||'div'));
		div.setAttribute('xlvobject','xlv.obj.ulli');
		return div;			
	},
	properties:{
		editable:false,
		selectable:true,
		defaultStyle:true,
		ulLevel:999,
		dmName:null,
		dmPath:null,
		dmSynchronized:true,
		nodeAttributeName:null,
		leafAttributeName:null,
		viewer:{objectName:'viewer'},
		viewerLeaf:{objectName:'viewer'},
		editor:null,
		editorLeaf:{objectName:'input'}
	},
	method:{
		construct:function(){
			if(this.$P.defaultStyle) Element.addClassName(this,'default_style');
			var v=this.$V;
			v.rootUL=xlv.util.findDirectChild(this,{tag:'ul'});
			if(this.$P.dmName!=null&& v.rootUL){
				v._pattern=v.rootUL;
				xlv.sys._clearExtendedFlag(v._pattern);
				v.rootUL.parentNode.removeChild(v.rootUL);
				v.rootUL=null;
			}
			if(!v.rootUL){
				v.rootUL=document.createElement('ul');
				this.appendChild(v.rootUL);
			}
			v.rootUL.setAttribute('id','xlvid_ulli_root');
			this.styleForId(v.rootUL,v.rootUL.id);
		},
		initial:function(){
			this.edc_properties.typeToStart='dblclick';
			this.editable(this.$P.editable);
			this.selectable(this.$P.selectable);
		},
		selectable:function(able){
			this.select_enabled(able);
		},
		editable:function(enable){
			this.edc_enabled(enable);
		},
		styleForId:function(root_ul,id){
		},
		expand:function(li){
			var ul=xlv.util.findDirectChild(li,{tag:'ul'});
			if(this.$P.dmName && ul && !ul._created){
				var indexes=xlv.util.indexesForLI(li);
				var dm=this.mvc_getModel();
				if(dm){
					var node=dm.getNode(xlv.util.indexesToXPath(indexes),this._getRootNode(dm));
					this._createLIforChildren(dm,node,ul);
				}
				ul._created=true;
			}
		},
		_runtimeStyle:function(ul,refresh){
		},
		//select imp
		select_changed:function(to_item,from_item){
			var dm=this.mvc_getModel(this.$P.dmName);
			if(dm&&this.$P.dmSynchronized) {
				if(to_item){
					var indexes=xlv.util.indexesForLI(to_item);
					var node=dm.getNode(xlv.util.indexesToXPath(indexes),this._getRootNode(dm));
					dm.select(node);
				}else dm.select(null);
			}
			this.$super('xlv.imp.select.select_changed',to_item,from_item);
		},
		// mvc implement
		_getRootNode:function(dm){
			if(this.$P.dmName){
				dm=(dm||this.mvc_getModel());
				return (this.$P.dmPath)?dm.getNode(this.$P.dmPath):dm.getNode(dm.$P.propXPath);
			}
			return null;
		},
		_createLIFromPattern:function(indexes){
			var li=null,ul=this.$V._pattern;
			if(ul){
				indexes=indexes.split(/\./);
				var found=false;
				for(var i=0;i<indexes.length;++i){
					if(ul && ul.hasChildNodes()){
						for(var c=0,count=0;c<ul.childNodes.length;++c){
							if(xlv.util.isTag(ul.childNodes[c],'li')){
								++count;
								li=ul.childNodes[c];
							}
							if(count==indexes[i])break;
						}
					}
					if(!li||!ul) break;
					ul=xlv.util.findDirectChild(li,{tag:'ul'},{xlvobject:'*'});
				}
				if(li){
					li=li.cloneNode(true);
					////????????????fix for IE
					if(Prototype.Browser.IE)xlv.sys._patchIEProperties(li);
					ul=xlv.util.findDirectChild(li,{tag:'ul'},{xlvobject:'*'});
					if(ul)ul.parentNode.removeChild(ul);
				}
			}
			return (li)?li:document.createElement('li');
		},
		_createLIforChildren:function(dm,node,child_ul){
			if(node&&node.hasChildNodes()&&child_ul){
				dm=(dm||this.mvc_getModel());
				var root_node=this._getRootNode(dm);
				var indexes=dm.nodeIndexes(node,root_node);
				var generate_ul=(xlv.util.levelOfDescendant(this,child_ul,{tag:'ul'})<this.$P.ulLevel);
				var lis=xlv.util.children(child_ul,{tag:'li'});
				var count=0;
				for(var i=0,n=node.childNodes.length;i<n;++i){
					var child=node.childNodes[i];
					if(child.nodeType != 1)continue;
					var li=null;
					var leaf=(!xlv.util.hasElementChildNode(child)||!generate_ul);
					if(++count>lis.length) {
						li=this._createLIFromPattern(indexes+((indexes=='')?'':'.')+count);
						child_ul.appendChild(li);
					}else li=lis[count-1];
					if(generate_ul&&!leaf){
						var ul=xlv.util.findDirectChild(li,{tag:'ul'});
						if(ul==null){
							ul=document.createElement('ul');
							li.appendChild(ul);
						}
						ul._created=false;
					}
					var child_indexes=indexes+((indexes=='')?'':'.')+count;
					this.viewerFor(dm,li,child_indexes,leaf);
					this.mvc_valueToView(this.mvc_valueFromModel(dm,child_indexes),child_indexes);
					this.styleFor(li,child_indexes,leaf);
				}
				for(var i=lis.length-1;i>=count;--i)this._disposeLI(lis[i]);
				child_ul._created=true;
			}
		},
		_disposeLI:function(li){
			var ul=xlv.util.findDirectChild(li,{tag:'ul'});
			if(ul) this._disposeUL(ul);
			if(li.viewer){
				xlv.sys.dispose(li.viewer,true);
				li.viewer=null;
			}
			li.parentNode.removeChild(li);
		},
		_disposeUL:function(ul){
			var lis=xlv.util.children(ul,{tag:'li'});
			for(var i=lis.length-1;i>=0;--i) {
				if(Prototype.Browser.IE)
					xlv.obj.ulli.method._disposeLI.call(this,lis[i]); //IE overflow
				else this._disposeLI(lis[i]);
			}
			ul.parentNode.removeChild(ul);
		},
		viewerFor:function(dm,li,indexes,leaf){
			if(!li.viewer){
				li.viewer=xlv.util.findDirectChild(li,{xlvobject:'*'});
				if(li.viewer) xlv.sys.shell(li.viewer);
				else{	
					var viewer=(leaf)?this.$P.viewerLeaf:this.$P.viewer;
					var ul=xlv.util.findDirectChild(li,{tag:'ul'});
					if(viewer)li.viewer=xlv.sys.newInstance(li,viewer.objectName,viewer.properties,viewer.method,ul);
				}
			}
		},
		styleFor:function(li,indexes,leaf){
			li.setAttribute('unselectable','on');
		},
		mvc_valueFromModel:function(dm,indexes){
			return dm.getNode(xlv.util.indexesToXPath(indexes),this._getRootNode(dm));
		},
		mvc_valueToModel:function(value,dm,indexes){
			if(Object.isElement(value))	dm.replace(value,xlv.util.indexesToXPath(indexes),this._getRootNode(dm));
			else{
				var leaf=xlv.util.isLeafAtIndexes(this.$V.rootUL,indexes);
				if(leaf){
					if(this.$P.leafAttributeName){
						  dm.setValue(value,xlv.util.indexesToXPath(indexes)+this.$P.leafAttributeName,this._getRootNode(dm));
					}else dm.setValue(value,xlv.util.indexesToXPath(indexes),this._getRootNode(dm));
				}
				else{
					var node=dm.getNode(xlv.util.indexesToXPath(indexes),this._getRootNode(dm));
					var new_node=node.cloneNode(false);
					if(this.$P.nodeAttributeName) new_node.setAttribute(this.$P.nodeAttributeName,value);
				}
			}
		},
		mvc_beforeModelChanged:function(dm,notify,memo){
			if(!this.mvc_isModel(dm))return;
			var v=this.$V,indexes,li;
			switch(notify){
			case 'setChanged':
					this.select_clearAll(null,false);
					break;
			case 'nodeChanged':	
			case 'rowChanged':
				if(memo.type=='remove'||memo.type=='removed'){
					indexes=dm.nodeIndexes(memo.src,this._getRootNode(dm));
					li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
					if(li){
						if(memo.type=='remove') Element.addClassName(li,'removed');
						else this._disposeLI(li);
					}
					break;
				}
				if(memo.type== 'cancel'){
					indexes=dm.nodeIndexes(memo.src,this._getRootNode(dm));
					li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
					if(li){
						Element.removeClassName(li,'inserted');
						if(memo.value=='insert')this._disposeLI(li);
					}
					break;
				}
			}
		},
		mvc_modelChanged:function(dm,notify,memo){
			switch(notify){
			case 'nodeChanged':
				if(memo.type=='update'){
					var indexes=dm.nodeIndexes(memo.src,this._getRootNode(dm));
					var li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
					if(li){
						Element.addClassName(li,'updated');
						this.mvc_valueToView(this.mvc_valueFromModel(dm,indexes),indexes);
					}
				}
				break;
			}
		},
		mvc_afterModelChanged:function(dm,notify,memo){
			if(!this.mvc_isModel(dm))return;
			var v=this.$V;
			switch(notify){
				case 'setChanged':
					var root_node=this._getRootNode(dm);
					if(root_node){
						var ul=this.$V.rootUL;
						if(ul._created)	while(ul.firstChild) this._disposeLI(ul.firstChild);
						ul._created=false;
						this._createLIforChildren(dm,root_node,ul);
						this._runtimeStyle(ul,true);
					}
					break;
				case 'rowChanged':
					var indexes=dm.nodeIndexes(memo.src,this._getRootNode(dm));
					if(memo.type=='inserted'||memo.type=='updated'){
						var li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
						if(li) {
							Element.removeClassName(li,'inserted');
							Element.removeClassName(li,'updated');
							if(dm.isExtend('xlv.obj.rowset')){
								var lis=xlv.util.children(li,{tag:'li'});
								for(var i=0;i<lis.length;++i)Element.removeClassName(lis[i],'updated');
							}
						}
						break;
					}
					if(memo.type== 'insert'){
						var ul=null,last_index=null;
						var dot=indexes.lastIndexOf('.');
						if(dot<0) {
							ul=this.$V.rootUL;
							last_index=indexes;
						}else{
							var li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes.substring(0,dot+1));
							if(li)ul=xlv.util.findDirectChild(li,{tag:'ul'});
							last_index=indexes.substring(dot);
						}
						if(ul&&ul._created){
							var li=document.createElement('li');
							Element.addClassName(li,'inserted');
							ul.insertBefore(li,xlv.util.findLIAtIndexes(ul,last_index));
							var leaf=!xlv.util.hasElementChildNode(memo.src);
							if(leaf) li.appendChild(document.createElement('ul'));
							this.viewerFor(dm, li, indexes, leaf);
							this.mvc_valueToView(this.mvc_valueFromModel(dm,indexes),indexes);
							this.styleFor(li, indexes, leaf);
							if(dm.isExtend('xlv.obj.rowset')){
								var child_ul=xlv.util.findDirectChild(li,{tag:'ul'});
								this._createLIforChildren(dm,memo.src,child_ul);
								this._runtimeStyle(child_ul,true);
							}
							this._runtimeStyle(ul,true);
						}
						break;
					}
					if(memo.type== 'cancel'){
						if(memo.value=='update'||memo.value=='remove'){
							var li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
							if(li){
								Element.removeClassName(li,'updated');
								Element.removeClassName(li,'removed');
								this.mvc_valueToView(this.mvc_valueFromModel(dm,indexes),indexes);
								if(dm.isExtend('xlv.obj.rowset')){
									var ul=xlv.util.findDirectChild(li,{tag:'ul'});
									if(ul&&ul._created){
										while(ul.firstChild) this._disposeLI(ul.firstChild);
										this._createLIforChildren(dm,memo.src,ul);
										this._runtimeStyle(ul,true);
									}
								}
							}
						}
					}
					break;
				case 'cursorMove':
					if(this.$P.dmSynchronized) {
						var indexes=dm.nodeIndexes(memo.to,this._getRootNode(dm));
						var li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
						if(li) this.select_item(li,null,false);
						else {
							indexes=indexes.split(/\./);
							var ul=this.$V.rootUL;
							for(var i=0;i<indexes.length;++i){
								li=null;
								if(ul && ul.hasChildNodes()){
									for(var c=0,count=0;c<ul.childNodes.length;++c){
										if(xlv.util.isTag(ul.childNodes[c],'li'))++count;
										if(count==indexes[i]) {li=ul.childNodes[c];break;}
									}
								}
								if(!li)break; 
								this.expand(li,true);
								ul=xlv.util.findDirectChild(li,{tag:'ul'},{xlvobject:'*'});
							}
							if(li)this.select_item(li,null,false);
						}
					}
					break;
			}
			this.mvc_enableControl(dm);
		},
		mvc_valueFromView:function(indexes){
			var li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
			return (li)? ((li.viewer)? li.viewer.mvc_valueFromView(indexes):xlv.util.getElementValue(li)):'';
		},
		mvc_valueToView:function(node,indexes){
			var li=xlv.util.findLIAtIndexes(this.$V.rootUL,indexes);
			if(li&&Object.isElement(node)) {
				var value='';
				if(xlv.util.isLeafLI(li))
					 value=(this.$P.leafAttributeName)?node.getAttribute(this.$P.leafAttributeName):node.nodeName+' : '+xlv.util.getElementValue(node);
				else value=(this.$P.nodeAttributeName)?node.getAttribute(this.$P.nodeAttributeName):node.nodeName;
				if(li.viewer && li.viewer['mvc_valueToView'])li.viewer.mvc_valueToView(value,indexes);
				else xlv.util.setElementValue(li,value);
			}
		},
		//edc imp
		edc_editable:function(source){
			if(!this.$P.editable) return false;
			var li=this.targetElement(source,{tag:'li'},{tag:'ul'});
			if(li){
				var editor=(xlv.util.isLeafLI(li))?this.$P.editorLeaf:this.$P.editor;
				if(editor) return li;
				var viewer=(xlv.util.isLeafLI(li))?this.$P.viewerLeaf:this.$P.viewer;
				return (viewer&&viewer.isImplement('xlv.imp.editor'))?li:false;
			}
			return false;
		},
		edc_policy:function(source){
			var li=this.targetElement(source,{tag:'li'},{tag:'ul'});
			var editor=(xlv.util.isLeafLI(li))?this.$P.editorLeaf:this.$P.editor;
			if(editor) return xlv.util.getEditorPolicy(editor.objectName);
			var viewer=(xlv.util.isLeafLI(li))?this.$P.viewerLeaf:this.$P.viewer;
			if(viewer&&viewer.isImplement('xlv.imp.editor')) return {install:'viewer',dispose:'none',uninstall:'none'};
			return this.$super('xlv.imp.edc.edc_policy',source);
		},
		edc_valueFromSource:function(source){
			var li=this.targetElement(source,{tag:'li'},{tag:'ul'});
			var indexes=xlv.util.indexesForLI(li);
			if(this.$P.dmName){
				var value=this.mvc_valueFromModel(this.mvc_getModel(),indexes);
				if(Object.isElement(value)){
					return (xlv.util.isLeafLI(li))?
						((this.$P.leafAttributeName)? value.getAttribute(this.$P.leafAttributeName):xlv.util.getElementValue(value)):
						((this.$P.nodeAttributeName)? value.getAttribute(this.$P.nodeAttributeName):value.nodeName);
				}else return value;
			}
			else return this.mvc_valueFromView(indexes);
		},
		edc_valueToSource:function(value,source){
			var li=this.targetElement(source,{tag:'li'},{tag:'ul'});
			var indexes=xlv.util.indexesForLI(li);
			if(this.$P.dmName) this.mvc_valueToModel(value,this.mvc_getModel(),indexes);
			else this.mvc_valueToView(value,indexes);
		},
		edc_installEditor:function(value,policy){
			var p=this.edc_properties;
			var li=this.targetElement((p.source||this.edc_properties.source),{tag:'td'});
			if(policy.install=='viewer'){
				p.editor=li.viewer;
			}else{
				var editor=(xlv.util.isLeafLI(li))?this.$P.editorLeaf:this.$P.editor;
				var parent=(policy.install=='replace')?p.source:this.getDesktop();
				p.editor=xlv.sys.newInstance(parent,editor.objectName,editor.properties,editor.method);
				this.$super('xlv.imp.edc.edc_installEditor',value,policy);
			}
			return p.editor;
		},
		/*select implement*/
		select_selectable:function(target){
			var li=this.targetElement(target,{tag:'li'});
			if(li && li!=target && xlv.util.isDescendantOf(target,li)) return li;
			else return false;
		}
	}
};
